source('../env.R')
Skipping install of 'clootl' from a github remote, the SHA1 (2ed1650b) has not changed since last install.
  Use `force = TRUE` to force installation
community_data = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'community_assembly_metrics_using_relative_abundance.csv'))
Rows: 308 Columns: 10── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (10): mntd_standard, mntd_actual, mass_fdiv_standard, mass_fdiv_actual, beak_width_fdiv_standard, beak_width_fdiv_actual, hwi_fdiv_standard, hwi_fdiv_actu...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(community_data)
colnames(community_data)
 [1] "mntd_standard"            "mntd_actual"              "mass_fdiv_standard"       "mass_fdiv_actual"         "beak_width_fdiv_standard"
 [6] "beak_width_fdiv_actual"   "hwi_fdiv_standard"        "hwi_fdiv_actual"          "city_id"                  "urban_pool_size"         
min(community_data$mntd_standard)
[1] -2.33692
max(community_data$mntd_standard)
[1] 2.328448
min(community_data$beak_width_fdiv_standard)
[1] -2.685152
max(community_data$beak_width_fdiv_standard)
[1] 1.931681
min(community_data$hwi_fdiv_standard)
[1] -2.200336
max(community_data$hwi_fdiv_standard)
[1] 2.333383
min(community_data$mass_fdiv_standard)
[1] -2.377212
max(community_data$mass_fdiv_standard)
[1] 2.1073

Join on realms

city_to_realm = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv'))
Rows: 337 Columns: 2── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
community_data_with_realm = left_join(community_data, city_to_realm)
Joining with `by = join_by(city_id)`

Cities as points

city_points = st_centroid(read_sf(filename(CITY_DATA_OUTPUT_DIR, 'city_selection.shp'))) %>% left_join(community_data_with_realm)
Warning: st_centroid assumes attributes are constant over geometriesWarning: st_centroid does not give correct centroids for longitude/latitude dataJoining with `by = join_by(city_id)`
city_points_coords = st_coordinates(city_points)
city_points$latitude = city_points_coords[,1]
city_points$longitude = city_points_coords[,2]
world_map = read_country_boundaries()

Load community data, and create long format version

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2428 Columns: 7── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, ebird_species_name, seasonal, presence, origin
dbl (2): city_id, relative_abundance_proxy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
communities
community_summary = communities %>% group_by(city_id) %>% summarise(regional_pool_size = n(), urban_pool_size = sum(relative_abundance_proxy > 0))
community_summary

Load trait data

traits = read_csv(filename(TAXONOMY_OUTPUT_DIR, 'traits_ebird.csv'))
Rows: 332 Columns: 10── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): ebird_species_name, habitat, trophic_level, trophic_niche, primary_lifestyle
dbl (5): beak_width, hwi, mass, habitat_density, migration
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(traits)

Load realm geo

resolve = read_resolve()
head(resolve)
Simple feature collection with 6 features and 15 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -162.1547 ymin: -69.55876 xmax: 158.6167 ymax: 61.53428
Geodetic CRS:  WGS 84

Load spatial var

spatial_var = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'spatial_var.csv')) %>% filter(city_id %in% community_summary$city_id)
Rows: 337 Columns: 3── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (3): city_id, NMDS1, NMDS2
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
spatial_var

Summary metrics by Realm

test_required_values = function(name, df) {
  cat(paste(
    test_value_wilcox(paste(name, 'MNTD'), df$mntd_standard),
    test_value_wilcox(paste(name, 'Beak Width FDiv'), df$beak_width_fdiv_standard),
    test_value_wilcox(paste(name, 'HWI FDiv'), df$hwi_fdiv_standard),
    test_value_wilcox(paste(name, 'Mass FDiv'), df$mass_fdiv_standard),
    paste('N', nrow(df)),
    sep = "\n"))
}
test_required_values('Global', community_data_with_realm)
Global MNTD median -0.36 ***
Global Beak Width FDiv median 0.02 
Global HWI FDiv median 0.39 **
Global Mass FDiv median 0.29 ***
N 308
unique(community_data_with_realm$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic"  "Indomalayan" "Australasia"
test_required_values('Nearctic', community_data_with_realm[community_data_with_realm$core_realm == 'Nearctic',])
Nearctic MNTD median 0.67 *
Nearctic Beak Width FDiv median 0.29 
Nearctic HWI FDiv median -0.8 ***
Nearctic Mass FDiv median -0.26 
N 46
test_required_values('Neotropic', community_data_with_realm[community_data_with_realm$core_realm == 'Neotropic',])
Neotropic MNTD median 0.03 
Neotropic Beak Width FDiv median -0.44 ***
Neotropic HWI FDiv median -0.31 
Neotropic Mass FDiv median 0.33 *
N 64
test_required_values('Palearctic', community_data_with_realm[community_data_with_realm$core_realm == 'Palearctic',])
Palearctic MNTD median 0.13 
Palearctic Beak Width FDiv median 1.25 ***
Palearctic HWI FDiv median -0.39 
Palearctic Mass FDiv median 0.01 
N 72
test_required_values('Afrotropic', community_data_with_realm[community_data_with_realm$core_realm == 'Afrotropic',])
Afrotropic MNTD median -1.28 *
Afrotropic Beak Width FDiv median -0.56 
Afrotropic HWI FDiv median 0.15 
Afrotropic Mass FDiv median -0.95 
N 9
test_required_values('Indomalayan', community_data_with_realm[community_data_with_realm$core_realm == 'Indomalayan',])
Indomalayan MNTD median -0.64 ***
Indomalayan Beak Width FDiv median -0.68 ***
Indomalayan HWI FDiv median 1.11 ***
Indomalayan Mass FDiv median 0.83 ***
N 111
test_required_values('Australasia', community_data_with_realm[community_data_with_realm$core_realm == 'Australasia',])
Australasia MNTD median -1.39 
Australasia Beak Width FDiv median -0.75 
Australasia HWI FDiv median 0.77 
Australasia Mass FDiv median -0.96 
N 6

Summary metrics by invasive species

cities_with_introduced_species = communities %>% filter(origin == 'Introduced') %>% select(city_id) %>% distinct()

cities_with_no_introduced_species = communities %>% filter(!(city_id %in% cities_with_introduced_species$city_id)) %>% select(city_id) %>% distinct()

cities_with_introduced_species$introduced_species = TRUE
cities_with_no_introduced_species$introduced_species = FALSE

community_data_with_realm_with_introduced = community_data_with_realm %>% left_join(rbind(cities_with_introduced_species, cities_with_no_introduced_species))
Joining with `by = join_by(city_id)`
community_data_with_realm_with_introduced
test_required_values('With Introduced', community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$introduced_species,])
With Introduced MNTD median -0.03 
With Introduced Beak Width FDiv median 0.11 
With Introduced HWI FDiv median -0.36 
With Introduced Mass FDiv median 0.01 
N 189
test_required_values('Without Introduced', community_data_with_realm_with_introduced[!community_data_with_realm_with_introduced$introduced_species,])
Without Introduced MNTD median -0.53 ***
Without Introduced Beak Width FDiv median -0.28 *
Without Introduced HWI FDiv median 1.04 ***
Without Introduced Mass FDiv median 0.72 ***
N 119

What families exist in which realms?

communities %>% 
  left_join(city_to_realm) %>% 
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family, core_realm) %>%
  distinct() %>%
  arrange(core_realm)
Joining with `by = join_by(city_id)`

Summary metrics by introduced species

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2428 Columns: 7── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, ebird_species_name, seasonal, presence, origin
dbl (2): city_id, relative_abundance_proxy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
city_introduced_species = communities %>% group_by(city_id) %>% summarise(number_of_species = n()) %>% left_join(
  communities %>% group_by(city_id) %>% filter(origin == 'Introduced') %>% summarise(number_of_introduced_species = n())
) %>% replace_na(list(number_of_introduced_species = 0))
Joining with `by = join_by(city_id)`
community_data_with_introductions = left_join(community_data, city_introduced_species)
Joining with `by = join_by(city_id)`
community_data_with_introductions$has_introduced_species = community_data_with_introductions$number_of_introduced_species > 0
community_data_with_introductions
community_data_with_introductions[,c('mntd_standard', 'has_introduced_species')]
community_data_with_introductions %>% group_by(has_introduced_species) %>% summarise(
  total_cities = n(), 
  
  mean_mntd_std = mean(mntd_standard, na.rm = T),
  median_mntd_std = median(mntd_standard, na.rm = T),
  sd_mntd_std = sd(mntd_standard, na.rm = T),
  
  mean_mass_fdiv_std = mean(mass_fdiv_standard, na.rm = T),
  median_mass_fdiv_std = median(mass_fdiv_standard, na.rm = T),
  sd_mass_fdiv_std = sd(mass_fdiv_standard, na.rm = T),
  
  mean_gape_width_fdiv_std = mean(beak_width_fdiv_standard, na.rm = T),
  median_gape_width_fdiv_std = median(beak_width_fdiv_standard, na.rm = T),
  sd_gape_width_fdiv_std = sd(beak_width_fdiv_standard, na.rm = T),
  
  mean_handwing_index_fdiv_std = mean(hwi_fdiv_standard, na.rm = T),
  median_handwing_index_fdiv_std = median(hwi_fdiv_standard, na.rm = T),
  sd_handwing_index_fdiv_std = sd(hwi_fdiv_standard, na.rm = T)
)

MNTD

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mntd_standard)) + geom_boxplot()

wilcox.test(mntd_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mntd_standard by has_introduced_species
W = 7925, p-value = 0.00001285
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.53±0.27) and those without (0.47±0.19) (p-value = 0.02).

Mass FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mass_fdiv_standard)) + geom_boxplot()

wilcox.test(mass_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mass_fdiv_standard by has_introduced_species
W = 15028, p-value = 0.0000006706
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.57±0.27) and those without (0.73±0.24) (p < 0.0001)

Beak Gape FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = beak_width_fdiv_standard)) + geom_boxplot()

wilcox.test(beak_width_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  beak_width_fdiv_standard by has_introduced_species
W = 8662, p-value = 0.0006884
alternative hypothesis: true location shift is not equal to 0

There is NOT a significant difference between the response of cities with introduced species (0.61±0.30) and those without (0.56±0.27)

HWI FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = hwi_fdiv_standard)) + geom_boxplot()

wilcox.test(hwi_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  hwi_fdiv_standard by has_introduced_species
W = 17606, p-value < 0.00000000000000022
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.49±0.30) and those without (0.79±0.21) (p < 0.0001)

Examine individual metrics

Analysis data frame

geography = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'geography.csv'))
Rows: 342 Columns: 26── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (26): city_id, city_avg_ndvi, city_avg_elevation, city_avg_temp, city_avg_min_monthly_temp, city_avg_max_monthly_temp, city_avg_monthly_temp, city_avg_rai...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
names(geography)
 [1] "city_id"                       "city_avg_ndvi"                 "city_avg_elevation"            "city_avg_temp"                
 [5] "city_avg_min_monthly_temp"     "city_avg_max_monthly_temp"     "city_avg_monthly_temp"         "city_avg_rainfall"            
 [9] "city_avg_max_monthly_rainfall" "city_avg_min_monthly_rainfall" "city_avg_soil_moisture"        "city_max_elev"                
[13] "city_min_elev"                 "city_elev_range"               "region_20km_avg_ndvi"          "region_20km_avg_elevation"    
[17] "region_20km_avg_soil_moisture" "region_20km_max_elev"          "region_20km_min_elev"          "region_20km_elev_range"       
[21] "region_50km_avg_ndvi"          "region_50km_avg_elevation"     "region_50km_avg_soil_moisture" "region_50km_max_elev"         
[25] "region_50km_min_elev"          "region_50km_elev_range"       
analysis_data = community_data_with_realm[,c('city_id', 'mntd_standard', 'mass_fdiv_standard', 'beak_width_fdiv_standard', 'hwi_fdiv_standard', 'core_realm')] %>% 
  left_join(city_points[,c('city_id', 'latitude', 'longitude')]) %>%
  left_join(community_data_with_introductions[,c('city_id', 'has_introduced_species')]) %>%
  left_join(geography) %>%
  left_join(spatial_var)
Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`
analysis_data$abs_latitude = abs(analysis_data$latitude)
analysis_data$core_realm = factor(analysis_data$core_realm, levels = c('Palearctic', 'Nearctic', 'Neotropic', 'Afrotropic', 'Indomalayan', 'Australasia', 'Oceania'))
analysis_data$has_introduced_species = factor(analysis_data$has_introduced_species, level = c('TRUE', 'FALSE'), labels = c('Introduced species', 'No introduced species'))
model_data = function(df, dependant_var) {
  df[,c(dependant_var, 'core_realm', 'abs_latitude', 'longitude', 'has_introduced_species', 'city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp', 'city_avg_min_monthly_temp', 'city_avg_max_monthly_temp', 'city_avg_monthly_temp', 'city_avg_rainfall', 'city_avg_max_monthly_rainfall', 'city_avg_min_monthly_rainfall', 'city_avg_soil_moisture', 'city_max_elev', 'city_min_elev', 'city_elev_range', 'region_20km_avg_ndvi', 'region_20km_avg_elevation', 'region_20km_avg_soil_moisture', 'region_20km_max_elev', 'region_20km_min_elev', 'region_20km_elev_range', 'region_50km_avg_ndvi', 'region_50km_avg_elevation', 'region_50km_avg_soil_moisture', 'region_50km_max_elev', 'region_50km_min_elev', 'region_50km_elev_range')]
}
model_data(analysis_data, 'mntd_standard')
names(analysis_data)
 [1] "city_id"                       "mntd_standard"                 "mass_fdiv_standard"            "beak_width_fdiv_standard"     
 [5] "hwi_fdiv_standard"             "core_realm"                    "latitude"                      "longitude"                    
 [9] "geometry"                      "has_introduced_species"        "city_avg_ndvi"                 "city_avg_elevation"           
[13] "city_avg_temp"                 "city_avg_min_monthly_temp"     "city_avg_max_monthly_temp"     "city_avg_monthly_temp"        
[17] "city_avg_rainfall"             "city_avg_max_monthly_rainfall" "city_avg_min_monthly_rainfall" "city_avg_soil_moisture"       
[21] "city_max_elev"                 "city_min_elev"                 "city_elev_range"               "region_20km_avg_ndvi"         
[25] "region_20km_avg_elevation"     "region_20km_avg_soil_moisture" "region_20km_max_elev"          "region_20km_min_elev"         
[29] "region_20km_elev_range"        "region_50km_avg_ndvi"          "region_50km_avg_elevation"     "region_50km_avg_soil_moisture"
[33] "region_50km_max_elev"          "region_50km_min_elev"          "region_50km_elev_range"        "NMDS1"                        
[37] "NMDS2"                         "abs_latitude"                 
all_explanatories = c(
    'city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp',
    'region_50km_avg_soil_moisture',
    'core_realmAfrotropic', 'core_realmAustralasia', 'core_realmIndomalayan', 'core_realmNearctic', 'core_realmNeotropic', 'core_realmPalearctic',
    'has_introduced_speciesNo introduced species'
)

all_explanatory_names = factor(
   c(
    'Avg. NDVI', 'Avg. Elevation', 'Avg. Temp.',
    'Avg. Soil Moisture',
    'Afrotropic', 'Australasia', 'Indomalayan', 'Nearctic', 'Neotropic', 'Palearctic',
    'No introduced species'
  ), ordered = T
)

explanatory_dictionary = data.frame(explanatory = all_explanatories, explanatory_name = all_explanatory_names)
  
with_explanatory_type_labels = function(p) {
  p = p[p$explanatory != '(Intercept)',]
  explanatory_levels = all_explanatories[all_explanatories %in% p$explanatory]
  p$explanatory <- factor(p$explanatory, levels = explanatory_levels)
  
  p$type <- 'Realm'
  p$type[p$explanatory %in% c('city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp')] <- 'City geography'
  p$type[p$explanatory %in% c('region_50km_avg_soil_moisture')] <- 'Regional (50km) geography'
  p$type[p$explanatory %in% c('has_introduced_speciesNo introduced species')] <- 'Species present'
  p
}

with_explanatory_names = function(p) {
  p %>% left_join(explanatory_dictionary)
}
explanatory_labels = c(
  'has_introduced_species'='Has introduced species', 
  'city_avg_ndvi'='Average NDVI', 
  'city_avg_elevation'='Average elevation', 
  'city_avg_temp'='Average temperature', 
  'city_avg_min_monthly_temp'='Average minimum monthly temperature', 
  'city_avg_max_monthly_temp'='Average maximum monthly temperature', 
  'city_avg_monthly_temp'='Average monthly temperature', 
  'city_avg_rainfall'='Average rainfall', 
  'city_avg_max_monthly_rainfall'='Average maximum monthly rainfall', 
  'city_avg_min_monthly_rainfall'='Average minimum monthly rainfall', 
  'city_avg_soil_moisture'='Average soil moisture', 
  'city_max_elev'='Maximum elevation', 
  'city_min_elev'='Minimum elevation', 
  'city_elev_range'='Elevation range', 
  'region_20km_avg_ndvi'='Average NDVI', 
  'region_20km_avg_elevation'='Average elevation', 
  'region_20km_avg_soil_moisture'='Average soil moisture', 
  'region_20km_max_elev'='Maximum elevation', 
  'region_20km_min_elev'='Minimum elevation',
  'region_20km_elev_range'='Elevation range',
  'region_50km_avg_ndvi'='Average NDVI',
  'region_50km_avg_elevation'='Average elevation',
  'region_50km_avg_soil_moisture'='Average soil moisture', 
  'region_50km_max_elev'='Maximum elevation',
  'region_50km_min_elev'='Minimum elevation', 
  'region_50km_elev_range'='Elevation range',
  'abs_latitude' = 'Absolute latitude',
  'latitude' = 'Latitude',
  'longitude' = 'Longitude',
  'core_realmAfrotropic' = 'Afrotropical', 
  'core_realmAustralasia' = 'Austaliasian', 
  'core_realmIndomalayan' = 'Indomalayan', 
  'core_realmNearctic' = 'Nearctic', 
  'core_realmNeotropic' = 'Neotropical',
  'core_realmPalearctic' = 'Palearctic',
  'core_realmOceania' = 'Oceanical')

Helper plot functions

geom_map = function(map_sf, title) {
  norm_mntd_analysis_geo = ggplot() + 
    geom_sf(data = world_map, aes(geometry = geometry)) +
    map_sf +
    standardised_colours_scale +
    labs(colour = 'Standardised\nResponse') +
    theme_bw() +
    theme(legend.position="bottom")
}

Spatial plot helpers

create_formula = function(response_var) {
  as.formula(paste(response_var, '~ core_realm + city_avg_ndvi + city_avg_elevation + city_avg_temp + region_50km_avg_soil_moisture + has_introduced_species'))
}

correlation_formula = as.formula('~ NMDS1 + NMDS2 + latitude + longitude')

gls_method = "ML"

spatial_model = function(formula, correlation) {
  gls(
    formula, 
    data = analysis_data, 
    correlation = correlation, 
    method = gls_method
  )
}

find_aics_for_correlations = function(formula) {
  data.frame(
    f = c('corExp', 'corLin', 'corRatio', 'corGaus', 'corSpher'),
    AIC = c(
      AIC(spatial_model(formula, corExp(form = correlation_formula))),
      AIC(spatial_model(formula, corLin(form = correlation_formula))),
      AIC(spatial_model(formula, corRatio(form = correlation_formula))),
      AIC(spatial_model(formula, corGaus(form = correlation_formula))),
      AIC(spatial_model(formula, corSpher(form = correlation_formula)))
    )
  )
}

plot_result = function(model_result) {
  model_summary = summary(model_result)
  result_table = as.data.frame(model_summary$tTable)
  result_table$explanatory = rownames(result_table)
  
  result_table = result_table %>% with_explanatory_type_labels() %>% with_explanatory_names()
  
  ggplot2::ggplot(result_table, ggplot2::aes(y=factor(explanatory_name, level = all_explanatory_names), x=Value, colour = type)) + 
    ggplot2::geom_line() +
    ggplot2::geom_point() +
    ggplot2::geom_errorbar(ggplot2::aes(xmin=Value-Std.Error, xmax=Value+Std.Error), width=.2,
                   position=ggplot2::position_dodge(0.05)) +
    ggplot2::theme_bw() +
    ggplot2::geom_vline(xintercept=0, linetype="dotted") +
    ggplot2::theme(legend.justification = "top") +
    ylab('Predictor') +
    guides(colour=guide_legend(title="Predictor type")) + xlab('Difference in response from 0\nhabitat filtering (< 0) and competitive interactions (> 0)\n± Standard Error') +
    scale_colour_manual(
      values = c(realm_colour, city_geography_colour, regional_50km_geography_colour, introduced_species_colour), 
      breaks = c('Realm', 'City geography', 'Regional (50km) geography', 'Species present'))
}

Polygons around realms in NMDS plot

cities_to_realms = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv')) %>% left_join(analysis_data) %>% filter(!is.na(NMDS1))
Rows: 337 Columns: 2── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining with `by = join_by(city_id, core_realm)`
unique(cities_to_realms$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic"  "Indomalayan" "Australasia"
realm_neartic_polygon = cities_to_realms %>% filter(core_realm == 'Nearctic') %>% slice(chull(NMDS1, NMDS2))
realm_neotropic_polygon = cities_to_realms %>% filter(core_realm == 'Neotropic') %>% slice(chull(NMDS1, NMDS2))
realm_palearctic_polygon = cities_to_realms %>% filter(core_realm == 'Palearctic') %>% slice(chull(NMDS1, NMDS2))
realm_afrotropic_polygon = cities_to_realms %>% filter(core_realm == 'Afrotropic') %>% slice(chull(NMDS1, NMDS2))
realm_indomalayan_polygon = cities_to_realms %>% filter(core_realm == 'Indomalayan') %>% slice(chull(NMDS1, NMDS2))
realm_australasia_polygon = cities_to_realms %>% filter(core_realm == 'Australasia') %>% slice(chull(NMDS1, NMDS2))

polygon_line_type = 'dashed'
polygon_linewidth = 0.4

with_realms = function(g) {
  g + 
    geom_polygon(data = realm_neartic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_neotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_palearctic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_afrotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_indomalayan_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_australasia_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0)
}

MNTD

std_mntd_analysis_spatial_plot =  ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mntd_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response")
std_mntd_analysis_spatial_plot = with_realms(std_mntd_analysis_spatial_plot)
std_mntd_analysis_spatial_plot

std_mntd_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = mntd_standard, geometry = geometry)), 'MNTD')
std_mntd_analysis_geo_plot

mntd_formula = create_formula('mntd_standard')
find_aics_for_correlations(mntd_formula)
mntd_spatial_model = spatial_model(mntd_formula, corLin(form = correlation_formula))
std_mntd_analysis_pred_plot = plot_result(mntd_spatial_model)
Joining with `by = join_by(explanatory)`
std_mntd_analysis_pred_plot

Gape width - FDiv

std_beak_width_fdiv_spatial_plot = ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = beak_width_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response")
std_beak_width_fdiv_spatial_plot = with_realms(std_beak_width_fdiv_spatial_plot)
std_beak_width_fdiv_spatial_plot

std_beak_width_fdiv_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = beak_width_fdiv_standard, geometry = geometry)), 'Beak Width FDiv')
std_beak_width_fdiv_analysis_geo_plot

beak_width_formula = create_formula('beak_width_fdiv_standard')
find_aics_for_correlations(beak_width_formula)
beak_width_spatial_model = spatial_model(beak_width_formula, corLin(form = correlation_formula))
std_beak_width_fdiv_analysis_pred_plot = plot_result(beak_width_spatial_model)
Joining with `by = join_by(explanatory)`
std_beak_width_fdiv_analysis_pred_plot

HWI - FDiv

std_hwi_fdiv_spatial_plot = ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = hwi_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response")
std_hwi_fdiv_spatial_plot = with_realms(std_hwi_fdiv_spatial_plot)
std_hwi_fdiv_spatial_plot

std_hwi_fdiv_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = hwi_fdiv_standard, geometry = geometry)), 'HWI FDiv')
std_hwi_fdiv_analysis_geo_plot

hwi_formula = create_formula('hwi_fdiv_standard')
find_aics_for_correlations(hwi_formula)
hwi_spatial_model = spatial_model(hwi_formula, corLin(form = correlation_formula))
std_hwi_fdiv_analysis_pred_plot = plot_result(hwi_spatial_model)
Joining with `by = join_by(explanatory)`
std_hwi_fdiv_analysis_pred_plot

Mass - FDiv

std_mass_fdiv_spatial_plot = ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = hwi_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response")
std_mass_fdiv_spatial_plot = with_realms(std_mass_fdiv_spatial_plot)
std_mass_fdiv_spatial_plot

std_mass_fdiv_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = mass_fdiv_standard, geometry = geometry)), 'Mass FDiv')
std_mass_fdiv_analysis_geo_plot

mass_formula = create_formula('mass_fdiv_standard')
find_aics_for_correlations(mass_formula)
mass_spatial_model = spatial_model(mass_formula, corGaus(form = correlation_formula))
std_mass_fdiv_analysis_pred_plot = plot_result(mass_spatial_model)
Joining with `by = join_by(explanatory)`
std_mass_fdiv_analysis_pred_plot

Create plot of differences in process response

pred_legend <- ggpubr::get_legend(
  # create some space to the left of the legend
  std_hwi_fdiv_analysis_pred_plot + theme(legend.box.margin = margin(0, 0, 0, 0)) + guides(colour=guide_legend(ncol=2)) + labs(color = "Predictor type")
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
geo_legend <- ggpubr::get_legend(
  # create some space to the left of the legend
  std_mass_fdiv_analysis_geo_plot + theme(legend.box.margin = margin(-80, 0, 0, 12), legend.title.position = "top", legend.key.width = unit(10, 'mm')) + labs(color = "Standardised response")
)

legend = plot_grid(
  geo_legend,
  pred_legend, 
  nrow = 1
)
legend

plot_grid(
  plot_grid(
    std_mntd_analysis_geo_plot + theme(legend.position="none"), 
    std_mntd_analysis_spatial_plot + theme(legend.position="none"), 
    std_mntd_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("MNTD", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_beak_width_fdiv_analysis_geo_plot + theme(legend.position="none"), 
    std_beak_width_fdiv_spatial_plot + theme(legend.position="none"), 
    std_beak_width_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("Beak Width", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_hwi_fdiv_analysis_geo_plot + theme(legend.position="none"), 
    std_hwi_fdiv_spatial_plot + theme(legend.position="none"), 
    std_hwi_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("HWI", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_mass_fdiv_analysis_geo_plot + theme(legend.position="none"), 
    std_mass_fdiv_spatial_plot + theme(legend.position="none"), 
    std_mass_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("Mass", size = 16, angle = 90, x = 0.01, y = 0.5), 
  legend,
  nrow = 5
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
ggsave(filename(FIGURES_OUTPUT_DIR, 'process_response.jpg'), width = 4200, height = 3200, units = 'px')

Compare metrics against each other

ggplot(analysis_data, aes(x = beak_width_fdiv_standard, y = mntd_standard, colour = core_realm)) + 
  geom_point() +
  ylab("MNTD") + 
  xlab("Beak Width FDiv") +
  theme_bw() + labs(color = "Realm")

ggplot(analysis_data, aes(x = hwi_fdiv_standard, y = mntd_standard, colour = core_realm)) + 
  geom_point() +
  ylab("MNTD") + 
  xlab("HWI FDiv") +
  theme_bw() + labs(color = "Realm")

ggplot(analysis_data, aes(x = hwi_fdiv_standard, y = beak_width_fdiv_standard, colour = core_realm)) + 
  geom_point() +
  ylab("Beak Width FDiv") + 
  xlab("HWI FDiv") +
  theme_bw() + labs(color = "Realm")

mntd_fdiv_analysis = analysis_data %>% 
  dplyr::select(city_id,  mntd_standard, hwi_fdiv_standard, beak_width_fdiv_standard, mass_fdiv_standard) %>%
  left_join(community_summary) %>%
  mutate(urban_pool_perc = urban_pool_size * 100 / regional_pool_size)
Joining with `by = join_by(city_id)`
mntd_fdiv_analysis
ggpairs(mntd_fdiv_analysis %>% dplyr::select(mntd_standard, hwi_fdiv_standard, beak_width_fdiv_standard, mass_fdiv_standard, regional_pool_size, urban_pool_size, urban_pool_perc), columnLabels = c('MNTD', 'HWI FD', 'Bk FD', 'Mss FD', 'Region Rich.', 'Urban Rich.', '% Urban'))
ggsave(filename(FIGURES_OUTPUT_DIR, 'appendix_standarised_correlation.jpg'))
Saving 7.29 x 4.51 in image

LS0tCnRpdGxlOiAiTWV0cmljcyBmb3IgYXNzZXNzaW5nIGNvbW11bml0eSBhc3NlbWJseSBwcm9jZXNzZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawpiaWJsaW9ncmFwaHk6IC4uL3JlZi5iaWIgIAotLS0KCmBgYHtyfQpzb3VyY2UoJy4uL2Vudi5SJykKYGBgCgpgYGB7cn0KY29tbXVuaXR5X2RhdGEgPSByZWFkX2NzdihmaWxlbmFtZShDT01NVU5JVFlfT1VUUFVUX0RJUiwgJ2NvbW11bml0eV9hc3NlbWJseV9tZXRyaWNzX3VzaW5nX3JlbGF0aXZlX2FidW5kYW5jZS5jc3YnKSkKaGVhZChjb21tdW5pdHlfZGF0YSkKY29sbmFtZXMoY29tbXVuaXR5X2RhdGEpCmBgYAoKYGBge3J9Cm1pbihjb21tdW5pdHlfZGF0YSRtbnRkX3N0YW5kYXJkKQptYXgoY29tbXVuaXR5X2RhdGEkbW50ZF9zdGFuZGFyZCkKbWluKGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkKbWF4KGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkKbWluKGNvbW11bml0eV9kYXRhJGh3aV9mZGl2X3N0YW5kYXJkKQptYXgoY29tbXVuaXR5X2RhdGEkaHdpX2ZkaXZfc3RhbmRhcmQpCm1pbihjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQpCm1heChjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQpCmBgYAoKCkpvaW4gb24gcmVhbG1zCmBgYHtyfQpjaXR5X3RvX3JlYWxtID0gcmVhZF9jc3YoZmlsZW5hbWUoQ0lUWV9EQVRBX09VVFBVVF9ESVIsICdyZWFsbXMuY3N2JykpCmNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0gPSBsZWZ0X2pvaW4oY29tbXVuaXR5X2RhdGEsIGNpdHlfdG9fcmVhbG0pCmBgYAoKQ2l0aWVzIGFzIHBvaW50cwpgYGB7cn0KY2l0eV9wb2ludHMgPSBzdF9jZW50cm9pZChyZWFkX3NmKGZpbGVuYW1lKENJVFlfREFUQV9PVVRQVVRfRElSLCAnY2l0eV9zZWxlY3Rpb24uc2hwJykpKSAlPiUgbGVmdF9qb2luKGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0pCmNpdHlfcG9pbnRzX2Nvb3JkcyA9IHN0X2Nvb3JkaW5hdGVzKGNpdHlfcG9pbnRzKQpjaXR5X3BvaW50cyRsYXRpdHVkZSA9IGNpdHlfcG9pbnRzX2Nvb3Jkc1ssMV0KY2l0eV9wb2ludHMkbG9uZ2l0dWRlID0gY2l0eV9wb2ludHNfY29vcmRzWywyXQpgYGAKICAKYGBge3J9CndvcmxkX21hcCA9IHJlYWRfY291bnRyeV9ib3VuZGFyaWVzKCkKYGBgCgpMb2FkIGNvbW11bml0eSBkYXRhLCBhbmQgY3JlYXRlIGxvbmcgZm9ybWF0IHZlcnNpb24KYGBge3J9CmNvbW11bml0aWVzID0gcmVhZF9jc3YoZmlsZW5hbWUoQ09NTVVOSVRZX09VVFBVVF9ESVIsICdjb21tdW5pdGllc19mb3JfYW5hbHlzaXMuY3N2JykpCmNvbW11bml0aWVzCmBgYAoKYGBge3J9CmNvbW11bml0eV9zdW1tYXJ5ID0gY29tbXVuaXRpZXMgJT4lIGdyb3VwX2J5KGNpdHlfaWQpICU+JSBzdW1tYXJpc2UocmVnaW9uYWxfcG9vbF9zaXplID0gbigpLCB1cmJhbl9wb29sX3NpemUgPSBzdW0ocmVsYXRpdmVfYWJ1bmRhbmNlX3Byb3h5ID4gMCkpCmNvbW11bml0eV9zdW1tYXJ5CmBgYAoKTG9hZCB0cmFpdCBkYXRhCmBgYHtyfQp0cmFpdHMgPSByZWFkX2NzdihmaWxlbmFtZShUQVhPTk9NWV9PVVRQVVRfRElSLCAndHJhaXRzX2ViaXJkLmNzdicpKQpoZWFkKHRyYWl0cykKYGBgCgpMb2FkIHJlYWxtIGdlbwpgYGB7cn0KcmVzb2x2ZSA9IHJlYWRfcmVzb2x2ZSgpCmhlYWQocmVzb2x2ZSkKYGBgCkxvYWQgc3BhdGlhbCB2YXIKYGBge3J9CnNwYXRpYWxfdmFyID0gcmVhZF9jc3YoZmlsZW5hbWUoQ09NTVVOSVRZX09VVFBVVF9ESVIsICdzcGF0aWFsX3Zhci5jc3YnKSkgJT4lIGZpbHRlcihjaXR5X2lkICVpbiUgY29tbXVuaXR5X3N1bW1hcnkkY2l0eV9pZCkKc3BhdGlhbF92YXIKYGBgCgojIFN1bW1hcnkgbWV0cmljcyBieSBSZWFsbQpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMgPSBmdW5jdGlvbihuYW1lLCBkZikgewogIGNhdChwYXN0ZSgKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdNTlREJyksIGRmJG1udGRfc3RhbmRhcmQpLAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ0JlYWsgV2lkdGggRkRpdicpLCBkZiRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQpLAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ0hXSSBGRGl2JyksIGRmJGh3aV9mZGl2X3N0YW5kYXJkKSwKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdNYXNzIEZEaXYnKSwgZGYkbWFzc19mZGl2X3N0YW5kYXJkKSwKICAgIHBhc3RlKCdOJywgbnJvdyhkZikpLAogICAgc2VwID0gIlxuIikpCn0KYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0dsb2JhbCcsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0pCmBgYAoKYGBge3J9CnVuaXF1ZShjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdOZWFyY3RpYycsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtID09ICdOZWFyY3RpYycsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ05lb3Ryb3BpYycsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtID09ICdOZW90cm9waWMnLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdQYWxlYXJjdGljJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ1BhbGVhcmN0aWMnLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdBZnJvdHJvcGljJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ0Fmcm90cm9waWMnLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdJbmRvbWFsYXlhbicsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtID09ICdJbmRvbWFsYXlhbicsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0F1c3RyYWxhc2lhJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ0F1c3RyYWxhc2lhJyxdKQpgYGAKCgojIFN1bW1hcnkgbWV0cmljcyBieSBpbnZhc2l2ZSBzcGVjaWVzCmBgYHtyfQpjaXRpZXNfd2l0aF9pbnRyb2R1Y2VkX3NwZWNpZXMgPSBjb21tdW5pdGllcyAlPiUgZmlsdGVyKG9yaWdpbiA9PSAnSW50cm9kdWNlZCcpICU+JSBzZWxlY3QoY2l0eV9pZCkgJT4lIGRpc3RpbmN0KCkKCmNpdGllc193aXRoX25vX2ludHJvZHVjZWRfc3BlY2llcyA9IGNvbW11bml0aWVzICU+JSBmaWx0ZXIoIShjaXR5X2lkICVpbiUgY2l0aWVzX3dpdGhfaW50cm9kdWNlZF9zcGVjaWVzJGNpdHlfaWQpKSAlPiUgc2VsZWN0KGNpdHlfaWQpICU+JSBkaXN0aW5jdCgpCgpjaXRpZXNfd2l0aF9pbnRyb2R1Y2VkX3NwZWNpZXMkaW50cm9kdWNlZF9zcGVjaWVzID0gVFJVRQpjaXRpZXNfd2l0aF9ub19pbnRyb2R1Y2VkX3NwZWNpZXMkaW50cm9kdWNlZF9zcGVjaWVzID0gRkFMU0UKCmNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSAlPiUgbGVmdF9qb2luKHJiaW5kKGNpdGllc193aXRoX2ludHJvZHVjZWRfc3BlY2llcywgY2l0aWVzX3dpdGhfbm9faW50cm9kdWNlZF9zcGVjaWVzKSkKY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQKYGBgCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnV2l0aCBJbnRyb2R1Y2VkJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWRbY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdXaXRob3V0IEludHJvZHVjZWQnLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZFshY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzLF0pCmBgYAoKIyBXaGF0IGZhbWlsaWVzIGV4aXN0IGluIHdoaWNoIHJlYWxtcz8KYGBge3J9CmNvbW11bml0aWVzICU+JSAKICBsZWZ0X2pvaW4oY2l0eV90b19yZWFsbSkgJT4lIAogIG11dGF0ZShmYW1pbHkgPSBnc3ViKCAiIC4qJCIsICIiLCBlYmlyZF9zcGVjaWVzX25hbWUpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGZhbWlseSwgY29yZV9yZWFsbSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKGNvcmVfcmVhbG0pCmBgYAoKIyBTdW1tYXJ5IG1ldHJpY3MgYnkgaW50cm9kdWNlZCBzcGVjaWVzCmBgYHtyfQpjb21tdW5pdGllcyA9IHJlYWRfY3N2KGZpbGVuYW1lKENPTU1VTklUWV9PVVRQVVRfRElSLCAnY29tbXVuaXRpZXNfZm9yX2FuYWx5c2lzLmNzdicpKQpjaXR5X2ludHJvZHVjZWRfc3BlY2llcyA9IGNvbW11bml0aWVzICU+JSBncm91cF9ieShjaXR5X2lkKSAlPiUgc3VtbWFyaXNlKG51bWJlcl9vZl9zcGVjaWVzID0gbigpKSAlPiUgbGVmdF9qb2luKAogIGNvbW11bml0aWVzICU+JSBncm91cF9ieShjaXR5X2lkKSAlPiUgZmlsdGVyKG9yaWdpbiA9PSAnSW50cm9kdWNlZCcpICU+JSBzdW1tYXJpc2UobnVtYmVyX29mX2ludHJvZHVjZWRfc3BlY2llcyA9IG4oKSkKKSAlPiUgcmVwbGFjZV9uYShsaXN0KG51bWJlcl9vZl9pbnRyb2R1Y2VkX3NwZWNpZXMgPSAwKSkKCmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyA9IGxlZnRfam9pbihjb21tdW5pdHlfZGF0YSwgY2l0eV9pbnRyb2R1Y2VkX3NwZWNpZXMpCmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyRoYXNfaW50cm9kdWNlZF9zcGVjaWVzID0gY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zJG51bWJlcl9vZl9pbnRyb2R1Y2VkX3NwZWNpZXMgPiAwCmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucwpgYGAKCmBgYHtyfQpjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnNbLGMoJ21udGRfc3RhbmRhcmQnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llcycpXQpgYGAKCmBgYHtyfQpjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMgJT4lIGdyb3VwX2J5KGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMpICU+JSBzdW1tYXJpc2UoCiAgdG90YWxfY2l0aWVzID0gbigpLCAKICAKICBtZWFuX21udGRfc3RkID0gbWVhbihtbnRkX3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIG1lZGlhbl9tbnRkX3N0ZCA9IG1lZGlhbihtbnRkX3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIHNkX21udGRfc3RkID0gc2QobW50ZF9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICAKICBtZWFuX21hc3NfZmRpdl9zdGQgPSBtZWFuKG1hc3NfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBtZWRpYW5fbWFzc19mZGl2X3N0ZCA9IG1lZGlhbihtYXNzX2ZkaXZfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfbWFzc19mZGl2X3N0ZCA9IHNkKG1hc3NfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICAKICBtZWFuX2dhcGVfd2lkdGhfZmRpdl9zdGQgPSBtZWFuKGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBtZWRpYW5fZ2FwZV93aWR0aF9mZGl2X3N0ZCA9IG1lZGlhbihiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfZ2FwZV93aWR0aF9mZGl2X3N0ZCA9IHNkKGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICAKICBtZWFuX2hhbmR3aW5nX2luZGV4X2ZkaXZfc3RkID0gbWVhbihod2lfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBtZWRpYW5faGFuZHdpbmdfaW5kZXhfZmRpdl9zdGQgPSBtZWRpYW4oaHdpX2ZkaXZfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfaGFuZHdpbmdfaW5kZXhfZmRpdl9zdGQgPSBzZChod2lfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKQopCmBgYAoKIyMgTU5URApgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gbW50ZF9zdGFuZGFyZCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QobW50ZF9zdGFuZGFyZCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNwb25zZSBvZiBjaXRpZXMgd2l0aCBpbnRyb2R1Y2VkIHNwZWNpZXMgKDAuNTPCsTAuMjcpIGFuZCB0aG9zZSB3aXRob3V0ICgwLjQ3wrEwLjE5KSAocC12YWx1ZSA9IDAuMDIpLgoKCiMjIE1hc3MgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gbWFzc19mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChtYXNzX2ZkaXZfc3RhbmRhcmQgfiBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMsIG5hLmFjdGlvbiA9ICduYS5vbWl0JykKYGBgClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNwb25zZSBvZiBjaXRpZXMgd2l0aCBpbnRyb2R1Y2VkIHNwZWNpZXMgKDAuNTfCsTAuMjcpIGFuZCB0aG9zZSB3aXRob3V0ICgwLjczwrEwLjI0KSAocCA8IDAuMDAwMSkKCgojIyBCZWFrIEdhcGUgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gYmVha193aWR0aF9mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQgfiBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMsIG5hLmFjdGlvbiA9ICduYS5vbWl0JykKYGBgClRoZXJlIGlzIE5PVCBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmVzcG9uc2Ugb2YgY2l0aWVzIHdpdGggaW50cm9kdWNlZCBzcGVjaWVzICgwLjYxwrEwLjMwKSBhbmQgdGhvc2Ugd2l0aG91dCAoMC41NsKxMC4yNykKCgojIyBIV0kgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gaHdpX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CndpbGNveC50ZXN0KGh3aV9mZGl2X3N0YW5kYXJkIH4gaGFzX2ludHJvZHVjZWRfc3BlY2llcywgY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBuYS5hY3Rpb24gPSAnbmEub21pdCcpCmBgYApUaGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmVzcG9uc2Ugb2YgY2l0aWVzIHdpdGggaW50cm9kdWNlZCBzcGVjaWVzICgwLjQ5wrEwLjMwKSBhbmQgdGhvc2Ugd2l0aG91dCAoMC43OcKxMC4yMSkgKHAgPCAwLjAwMDEpCgoKIyBFeGFtaW5lIGluZGl2aWR1YWwgbWV0cmljcwoKIyMgQW5hbHlzaXMgZGF0YSBmcmFtZQpgYGB7cn0KZ2VvZ3JhcGh5ID0gcmVhZF9jc3YoZmlsZW5hbWUoQ0lUWV9EQVRBX09VVFBVVF9ESVIsICdnZW9ncmFwaHkuY3N2JykpCm5hbWVzKGdlb2dyYXBoeSkKYGBgCgpgYGB7cn0KYW5hbHlzaXNfZGF0YSA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bLGMoJ2NpdHlfaWQnLCAnbW50ZF9zdGFuZGFyZCcsICdtYXNzX2ZkaXZfc3RhbmRhcmQnLCAnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJywgJ2h3aV9mZGl2X3N0YW5kYXJkJywgJ2NvcmVfcmVhbG0nKV0gJT4lIAogIGxlZnRfam9pbihjaXR5X3BvaW50c1ssYygnY2l0eV9pZCcsICdsYXRpdHVkZScsICdsb25naXR1ZGUnKV0pICU+JQogIGxlZnRfam9pbihjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnNbLGMoJ2NpdHlfaWQnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llcycpXSkgJT4lCiAgbGVmdF9qb2luKGdlb2dyYXBoeSkgJT4lCiAgbGVmdF9qb2luKHNwYXRpYWxfdmFyKQoKYW5hbHlzaXNfZGF0YSRhYnNfbGF0aXR1ZGUgPSBhYnMoYW5hbHlzaXNfZGF0YSRsYXRpdHVkZSkKYW5hbHlzaXNfZGF0YSRjb3JlX3JlYWxtID0gZmFjdG9yKGFuYWx5c2lzX2RhdGEkY29yZV9yZWFsbSwgbGV2ZWxzID0gYygnUGFsZWFyY3RpYycsICdOZWFyY3RpYycsICdOZW90cm9waWMnLCAnQWZyb3Ryb3BpYycsICdJbmRvbWFsYXlhbicsICdBdXN0cmFsYXNpYScsICdPY2VhbmlhJykpCmFuYWx5c2lzX2RhdGEkaGFzX2ludHJvZHVjZWRfc3BlY2llcyA9IGZhY3RvcihhbmFseXNpc19kYXRhJGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGxldmVsID0gYygnVFJVRScsICdGQUxTRScpLCBsYWJlbHMgPSBjKCdJbnRyb2R1Y2VkIHNwZWNpZXMnLCAnTm8gaW50cm9kdWNlZCBzcGVjaWVzJykpCmBgYAoKYGBge3J9Cm1vZGVsX2RhdGEgPSBmdW5jdGlvbihkZiwgZGVwZW5kYW50X3ZhcikgewogIGRmWyxjKGRlcGVuZGFudF92YXIsICdjb3JlX3JlYWxtJywgJ2Fic19sYXRpdHVkZScsICdsb25naXR1ZGUnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llcycsICdjaXR5X2F2Z19uZHZpJywgJ2NpdHlfYXZnX2VsZXZhdGlvbicsICdjaXR5X2F2Z190ZW1wJywgJ2NpdHlfYXZnX21pbl9tb250aGx5X3RlbXAnLCAnY2l0eV9hdmdfbWF4X21vbnRobHlfdGVtcCcsICdjaXR5X2F2Z19tb250aGx5X3RlbXAnLCAnY2l0eV9hdmdfcmFpbmZhbGwnLCAnY2l0eV9hdmdfbWF4X21vbnRobHlfcmFpbmZhbGwnLCAnY2l0eV9hdmdfbWluX21vbnRobHlfcmFpbmZhbGwnLCAnY2l0eV9hdmdfc29pbF9tb2lzdHVyZScsICdjaXR5X21heF9lbGV2JywgJ2NpdHlfbWluX2VsZXYnLCAnY2l0eV9lbGV2X3JhbmdlJywgJ3JlZ2lvbl8yMGttX2F2Z19uZHZpJywgJ3JlZ2lvbl8yMGttX2F2Z19lbGV2YXRpb24nLCAncmVnaW9uXzIwa21fYXZnX3NvaWxfbW9pc3R1cmUnLCAncmVnaW9uXzIwa21fbWF4X2VsZXYnLCAncmVnaW9uXzIwa21fbWluX2VsZXYnLCAncmVnaW9uXzIwa21fZWxldl9yYW5nZScsICdyZWdpb25fNTBrbV9hdmdfbmR2aScsICdyZWdpb25fNTBrbV9hdmdfZWxldmF0aW9uJywgJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJywgJ3JlZ2lvbl81MGttX21heF9lbGV2JywgJ3JlZ2lvbl81MGttX21pbl9lbGV2JywgJ3JlZ2lvbl81MGttX2VsZXZfcmFuZ2UnKV0KfQptb2RlbF9kYXRhKGFuYWx5c2lzX2RhdGEsICdtbnRkX3N0YW5kYXJkJykKYGBgCgpgYGB7cn0KbmFtZXMoYW5hbHlzaXNfZGF0YSkKYGBgCgpgYGB7cn0KYWxsX2V4cGxhbmF0b3JpZXMgPSBjKAogICAgJ2NpdHlfYXZnX25kdmknLCAnY2l0eV9hdmdfZWxldmF0aW9uJywgJ2NpdHlfYXZnX3RlbXAnLAogICAgJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJywKICAgICdjb3JlX3JlYWxtQWZyb3Ryb3BpYycsICdjb3JlX3JlYWxtQXVzdHJhbGFzaWEnLCAnY29yZV9yZWFsbUluZG9tYWxheWFuJywgJ2NvcmVfcmVhbG1OZWFyY3RpYycsICdjb3JlX3JlYWxtTmVvdHJvcGljJywgJ2NvcmVfcmVhbG1QYWxlYXJjdGljJywKICAgICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzTm8gaW50cm9kdWNlZCBzcGVjaWVzJwopCgphbGxfZXhwbGFuYXRvcnlfbmFtZXMgPSBmYWN0b3IoCiAgIGMoCiAgICAnQXZnLiBORFZJJywgJ0F2Zy4gRWxldmF0aW9uJywgJ0F2Zy4gVGVtcC4nLAogICAgJ0F2Zy4gU29pbCBNb2lzdHVyZScsCiAgICAnQWZyb3Ryb3BpYycsICdBdXN0cmFsYXNpYScsICdJbmRvbWFsYXlhbicsICdOZWFyY3RpYycsICdOZW90cm9waWMnLCAnUGFsZWFyY3RpYycsCiAgICAnTm8gaW50cm9kdWNlZCBzcGVjaWVzJwogICksIG9yZGVyZWQgPSBUCikKCmV4cGxhbmF0b3J5X2RpY3Rpb25hcnkgPSBkYXRhLmZyYW1lKGV4cGxhbmF0b3J5ID0gYWxsX2V4cGxhbmF0b3JpZXMsIGV4cGxhbmF0b3J5X25hbWUgPSBhbGxfZXhwbGFuYXRvcnlfbmFtZXMpCiAgCndpdGhfZXhwbGFuYXRvcnlfdHlwZV9sYWJlbHMgPSBmdW5jdGlvbihwKSB7CiAgcCA9IHBbcCRleHBsYW5hdG9yeSAhPSAnKEludGVyY2VwdCknLF0KICBleHBsYW5hdG9yeV9sZXZlbHMgPSBhbGxfZXhwbGFuYXRvcmllc1thbGxfZXhwbGFuYXRvcmllcyAlaW4lIHAkZXhwbGFuYXRvcnldCiAgcCRleHBsYW5hdG9yeSA8LSBmYWN0b3IocCRleHBsYW5hdG9yeSwgbGV2ZWxzID0gZXhwbGFuYXRvcnlfbGV2ZWxzKQogIAogIHAkdHlwZSA8LSAnUmVhbG0nCiAgcCR0eXBlW3AkZXhwbGFuYXRvcnkgJWluJSBjKCdjaXR5X2F2Z19uZHZpJywgJ2NpdHlfYXZnX2VsZXZhdGlvbicsICdjaXR5X2F2Z190ZW1wJyldIDwtICdDaXR5IGdlb2dyYXBoeScKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJyldIDwtICdSZWdpb25hbCAoNTBrbSkgZ2VvZ3JhcGh5JwogIHAkdHlwZVtwJGV4cGxhbmF0b3J5ICVpbiUgYygnaGFzX2ludHJvZHVjZWRfc3BlY2llc05vIGludHJvZHVjZWQgc3BlY2llcycpXSA8LSAnU3BlY2llcyBwcmVzZW50JwogIHAKfQoKd2l0aF9leHBsYW5hdG9yeV9uYW1lcyA9IGZ1bmN0aW9uKHApIHsKICBwICU+JSBsZWZ0X2pvaW4oZXhwbGFuYXRvcnlfZGljdGlvbmFyeSkKfQpgYGAKCmBgYHtyfQpleHBsYW5hdG9yeV9sYWJlbHMgPSBjKAogICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzJz0nSGFzIGludHJvZHVjZWQgc3BlY2llcycsIAogICdjaXR5X2F2Z19uZHZpJz0nQXZlcmFnZSBORFZJJywgCiAgJ2NpdHlfYXZnX2VsZXZhdGlvbic9J0F2ZXJhZ2UgZWxldmF0aW9uJywgCiAgJ2NpdHlfYXZnX3RlbXAnPSdBdmVyYWdlIHRlbXBlcmF0dXJlJywgCiAgJ2NpdHlfYXZnX21pbl9tb250aGx5X3RlbXAnPSdBdmVyYWdlIG1pbmltdW0gbW9udGhseSB0ZW1wZXJhdHVyZScsIAogICdjaXR5X2F2Z19tYXhfbW9udGhseV90ZW1wJz0nQXZlcmFnZSBtYXhpbXVtIG1vbnRobHkgdGVtcGVyYXR1cmUnLCAKICAnY2l0eV9hdmdfbW9udGhseV90ZW1wJz0nQXZlcmFnZSBtb250aGx5IHRlbXBlcmF0dXJlJywgCiAgJ2NpdHlfYXZnX3JhaW5mYWxsJz0nQXZlcmFnZSByYWluZmFsbCcsIAogICdjaXR5X2F2Z19tYXhfbW9udGhseV9yYWluZmFsbCc9J0F2ZXJhZ2UgbWF4aW11bSBtb250aGx5IHJhaW5mYWxsJywgCiAgJ2NpdHlfYXZnX21pbl9tb250aGx5X3JhaW5mYWxsJz0nQXZlcmFnZSBtaW5pbXVtIG1vbnRobHkgcmFpbmZhbGwnLCAKICAnY2l0eV9hdmdfc29pbF9tb2lzdHVyZSc9J0F2ZXJhZ2Ugc29pbCBtb2lzdHVyZScsIAogICdjaXR5X21heF9lbGV2Jz0nTWF4aW11bSBlbGV2YXRpb24nLCAKICAnY2l0eV9taW5fZWxldic9J01pbmltdW0gZWxldmF0aW9uJywgCiAgJ2NpdHlfZWxldl9yYW5nZSc9J0VsZXZhdGlvbiByYW5nZScsIAogICdyZWdpb25fMjBrbV9hdmdfbmR2aSc9J0F2ZXJhZ2UgTkRWSScsIAogICdyZWdpb25fMjBrbV9hdmdfZWxldmF0aW9uJz0nQXZlcmFnZSBlbGV2YXRpb24nLCAKICAncmVnaW9uXzIwa21fYXZnX3NvaWxfbW9pc3R1cmUnPSdBdmVyYWdlIHNvaWwgbW9pc3R1cmUnLCAKICAncmVnaW9uXzIwa21fbWF4X2VsZXYnPSdNYXhpbXVtIGVsZXZhdGlvbicsIAogICdyZWdpb25fMjBrbV9taW5fZWxldic9J01pbmltdW0gZWxldmF0aW9uJywKICAncmVnaW9uXzIwa21fZWxldl9yYW5nZSc9J0VsZXZhdGlvbiByYW5nZScsCiAgJ3JlZ2lvbl81MGttX2F2Z19uZHZpJz0nQXZlcmFnZSBORFZJJywKICAncmVnaW9uXzUwa21fYXZnX2VsZXZhdGlvbic9J0F2ZXJhZ2UgZWxldmF0aW9uJywKICAncmVnaW9uXzUwa21fYXZnX3NvaWxfbW9pc3R1cmUnPSdBdmVyYWdlIHNvaWwgbW9pc3R1cmUnLCAKICAncmVnaW9uXzUwa21fbWF4X2VsZXYnPSdNYXhpbXVtIGVsZXZhdGlvbicsCiAgJ3JlZ2lvbl81MGttX21pbl9lbGV2Jz0nTWluaW11bSBlbGV2YXRpb24nLCAKICAncmVnaW9uXzUwa21fZWxldl9yYW5nZSc9J0VsZXZhdGlvbiByYW5nZScsCiAgJ2Fic19sYXRpdHVkZScgPSAnQWJzb2x1dGUgbGF0aXR1ZGUnLAogICdsYXRpdHVkZScgPSAnTGF0aXR1ZGUnLAogICdsb25naXR1ZGUnID0gJ0xvbmdpdHVkZScsCiAgJ2NvcmVfcmVhbG1BZnJvdHJvcGljJyA9ICdBZnJvdHJvcGljYWwnLCAKICAnY29yZV9yZWFsbUF1c3RyYWxhc2lhJyA9ICdBdXN0YWxpYXNpYW4nLCAKICAnY29yZV9yZWFsbUluZG9tYWxheWFuJyA9ICdJbmRvbWFsYXlhbicsIAogICdjb3JlX3JlYWxtTmVhcmN0aWMnID0gJ05lYXJjdGljJywgCiAgJ2NvcmVfcmVhbG1OZW90cm9waWMnID0gJ05lb3Ryb3BpY2FsJywKICAnY29yZV9yZWFsbVBhbGVhcmN0aWMnID0gJ1BhbGVhcmN0aWMnLAogICdjb3JlX3JlYWxtT2NlYW5pYScgPSAnT2NlYW5pY2FsJykKYGBgCgojIyBIZWxwZXIgcGxvdCBmdW5jdGlvbnMKYGBge3J9Cmdlb21fbWFwID0gZnVuY3Rpb24obWFwX3NmLCB0aXRsZSkgewogIG5vcm1fbW50ZF9hbmFseXNpc19nZW8gPSBnZ3Bsb3QoKSArIAogICAgZ2VvbV9zZihkYXRhID0gd29ybGRfbWFwLCBhZXMoZ2VvbWV0cnkgPSBnZW9tZXRyeSkpICsKICAgIG1hcF9zZiArCiAgICBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArCiAgICBsYWJzKGNvbG91ciA9ICdTdGFuZGFyZGlzZWRcblJlc3BvbnNlJykgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCn0KYGBgCgoKIyMgU3BhdGlhbCBwbG90IGhlbHBlcnMKYGBge3J9CmNyZWF0ZV9mb3JtdWxhID0gZnVuY3Rpb24ocmVzcG9uc2VfdmFyKSB7CiAgYXMuZm9ybXVsYShwYXN0ZShyZXNwb25zZV92YXIsICd+IGNvcmVfcmVhbG0gKyBjaXR5X2F2Z19uZHZpICsgY2l0eV9hdmdfZWxldmF0aW9uICsgY2l0eV9hdmdfdGVtcCArIHJlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlICsgaGFzX2ludHJvZHVjZWRfc3BlY2llcycpKQp9Cgpjb3JyZWxhdGlvbl9mb3JtdWxhID0gYXMuZm9ybXVsYSgnfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUnKQoKZ2xzX21ldGhvZCA9ICJNTCIKCnNwYXRpYWxfbW9kZWwgPSBmdW5jdGlvbihmb3JtdWxhLCBjb3JyZWxhdGlvbikgewogIGdscygKICAgIGZvcm11bGEsIAogICAgZGF0YSA9IGFuYWx5c2lzX2RhdGEsIAogICAgY29ycmVsYXRpb24gPSBjb3JyZWxhdGlvbiwgCiAgICBtZXRob2QgPSBnbHNfbWV0aG9kCiAgKQp9CgpmaW5kX2FpY3NfZm9yX2NvcnJlbGF0aW9ucyA9IGZ1bmN0aW9uKGZvcm11bGEpIHsKICBkYXRhLmZyYW1lKAogICAgZiA9IGMoJ2NvckV4cCcsICdjb3JMaW4nLCAnY29yUmF0aW8nLCAnY29yR2F1cycsICdjb3JTcGhlcicpLAogICAgQUlDID0gYygKICAgICAgQUlDKHNwYXRpYWxfbW9kZWwoZm9ybXVsYSwgY29yRXhwKGZvcm0gPSBjb3JyZWxhdGlvbl9mb3JtdWxhKSkpLAogICAgICBBSUMoc3BhdGlhbF9tb2RlbChmb3JtdWxhLCBjb3JMaW4oZm9ybSA9IGNvcnJlbGF0aW9uX2Zvcm11bGEpKSksCiAgICAgIEFJQyhzcGF0aWFsX21vZGVsKGZvcm11bGEsIGNvclJhdGlvKGZvcm0gPSBjb3JyZWxhdGlvbl9mb3JtdWxhKSkpLAogICAgICBBSUMoc3BhdGlhbF9tb2RlbChmb3JtdWxhLCBjb3JHYXVzKGZvcm0gPSBjb3JyZWxhdGlvbl9mb3JtdWxhKSkpLAogICAgICBBSUMoc3BhdGlhbF9tb2RlbChmb3JtdWxhLCBjb3JTcGhlcihmb3JtID0gY29ycmVsYXRpb25fZm9ybXVsYSkpKQogICAgKQogICkKfQoKcGxvdF9yZXN1bHQgPSBmdW5jdGlvbihtb2RlbF9yZXN1bHQpIHsKICBtb2RlbF9zdW1tYXJ5ID0gc3VtbWFyeShtb2RlbF9yZXN1bHQpCiAgcmVzdWx0X3RhYmxlID0gYXMuZGF0YS5mcmFtZShtb2RlbF9zdW1tYXJ5JHRUYWJsZSkKICByZXN1bHRfdGFibGUkZXhwbGFuYXRvcnkgPSByb3duYW1lcyhyZXN1bHRfdGFibGUpCiAgCiAgcmVzdWx0X3RhYmxlID0gcmVzdWx0X3RhYmxlICU+JSB3aXRoX2V4cGxhbmF0b3J5X3R5cGVfbGFiZWxzKCkgJT4lIHdpdGhfZXhwbGFuYXRvcnlfbmFtZXMoKQogIAogIGdncGxvdDI6OmdncGxvdChyZXN1bHRfdGFibGUsIGdncGxvdDI6OmFlcyh5PWZhY3RvcihleHBsYW5hdG9yeV9uYW1lLCBsZXZlbCA9IGFsbF9leHBsYW5hdG9yeV9uYW1lcyksIHg9VmFsdWUsIGNvbG91ciA9IHR5cGUpKSArIAogICAgZ2dwbG90Mjo6Z2VvbV9saW5lKCkgKwogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludCgpICsKICAgIGdncGxvdDI6Omdlb21fZXJyb3JiYXIoZ2dwbG90Mjo6YWVzKHhtaW49VmFsdWUtU3RkLkVycm9yLCB4bWF4PVZhbHVlK1N0ZC5FcnJvciksIHdpZHRoPS4yLAogICAgICAgICAgICAgICAgICAgcG9zaXRpb249Z2dwbG90Mjo6cG9zaXRpb25fZG9kZ2UoMC4wNSkpICsKICAgIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogICAgZ2dwbG90Mjo6Z2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkb3R0ZWQiKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJ0b3AiKSArCiAgICB5bGFiKCdQcmVkaWN0b3InKSArCiAgICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJlZGljdG9yIHR5cGUiKSkgKyB4bGFiKCdEaWZmZXJlbmNlIGluIHJlc3BvbnNlIGZyb20gMFxuaGFiaXRhdCBmaWx0ZXJpbmcgKDwgMCkgYW5kIGNvbXBldGl0aXZlIGludGVyYWN0aW9ucyAoPiAwKVxuwrEgU3RhbmRhcmQgRXJyb3InKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKAogICAgICB2YWx1ZXMgPSBjKHJlYWxtX2NvbG91ciwgY2l0eV9nZW9ncmFwaHlfY29sb3VyLCByZWdpb25hbF81MGttX2dlb2dyYXBoeV9jb2xvdXIsIGludHJvZHVjZWRfc3BlY2llc19jb2xvdXIpLCAKICAgICAgYnJlYWtzID0gYygnUmVhbG0nLCAnQ2l0eSBnZW9ncmFwaHknLCAnUmVnaW9uYWwgKDUwa20pIGdlb2dyYXBoeScsICdTcGVjaWVzIHByZXNlbnQnKSkKfQpgYGAKCiMjIFBvbHlnb25zIGFyb3VuZCByZWFsbXMgaW4gTk1EUyBwbG90CmBgYHtyfQpjaXRpZXNfdG9fcmVhbG1zID0gcmVhZF9jc3YoZmlsZW5hbWUoQ0lUWV9EQVRBX09VVFBVVF9ESVIsICdyZWFsbXMuY3N2JykpICU+JSBsZWZ0X2pvaW4oYW5hbHlzaXNfZGF0YSkgJT4lIGZpbHRlcighaXMubmEoTk1EUzEpKQp1bmlxdWUoY2l0aWVzX3RvX3JlYWxtcyRjb3JlX3JlYWxtKQpyZWFsbV9uZWFydGljX3BvbHlnb24gPSBjaXRpZXNfdG9fcmVhbG1zICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnTmVhcmN0aWMnKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKcmVhbG1fbmVvdHJvcGljX3BvbHlnb24gPSBjaXRpZXNfdG9fcmVhbG1zICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnTmVvdHJvcGljJykgJT4lIHNsaWNlKGNodWxsKE5NRFMxLCBOTURTMikpCnJlYWxtX3BhbGVhcmN0aWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXMgJT4lIGZpbHRlcihjb3JlX3JlYWxtID09ICdQYWxlYXJjdGljJykgJT4lIHNsaWNlKGNodWxsKE5NRFMxLCBOTURTMikpCnJlYWxtX2Fmcm90cm9waWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXMgJT4lIGZpbHRlcihjb3JlX3JlYWxtID09ICdBZnJvdHJvcGljJykgJT4lIHNsaWNlKGNodWxsKE5NRFMxLCBOTURTMikpCnJlYWxtX2luZG9tYWxheWFuX3BvbHlnb24gPSBjaXRpZXNfdG9fcmVhbG1zICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnSW5kb21hbGF5YW4nKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKcmVhbG1fYXVzdHJhbGFzaWFfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXMgJT4lIGZpbHRlcihjb3JlX3JlYWxtID09ICdBdXN0cmFsYXNpYScpICU+JSBzbGljZShjaHVsbChOTURTMSwgTk1EUzIpKQoKcG9seWdvbl9saW5lX3R5cGUgPSAnZGFzaGVkJwpwb2x5Z29uX2xpbmV3aWR0aCA9IDAuNAoKd2l0aF9yZWFsbXMgPSBmdW5jdGlvbihnKSB7CiAgZyArIAogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9uZWFydGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbmVvdHJvcGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fcGFsZWFyY3RpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2Fmcm90cm9waWNfcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkgKwogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9pbmRvbWFsYXlhbl9wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2F1c3RyYWxhc2lhX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApCn0KYGBgCgojIyBNTlRECmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19zcGF0aWFsX3Bsb3QgPSAgZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gTk1EUzEsIHkgPSBOTURTMiwgY29sb3VyID0gbW50ZF9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKQpzdGRfbW50ZF9hbmFseXNpc19zcGF0aWFsX3Bsb3QgPSB3aXRoX3JlYWxtcyhzdGRfbW50ZF9hbmFseXNpc19zcGF0aWFsX3Bsb3QpCnN0ZF9tbnRkX2FuYWx5c2lzX3NwYXRpYWxfcGxvdApgYGAKCmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwKGdlb21fc2YoZGF0YSA9IGFuYWx5c2lzX2RhdGEsIGFlcyhjb2xvciA9IG1udGRfc3RhbmRhcmQsIGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSwgJ01OVEQnKQpzdGRfbW50ZF9hbmFseXNpc19nZW9fcGxvdApgYGAKCmBgYHtyfQptbnRkX2Zvcm11bGEgPSBjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpCmZpbmRfYWljc19mb3JfY29ycmVsYXRpb25zKG1udGRfZm9ybXVsYSkKYGBgCgpgYGB7cn0KbW50ZF9zcGF0aWFsX21vZGVsID0gc3BhdGlhbF9tb2RlbChtbnRkX2Zvcm11bGEsIGNvckxpbihmb3JtID0gY29ycmVsYXRpb25fZm9ybXVsYSkpCmBgYAoKYGBge3J9CnN0ZF9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfcmVzdWx0KG1udGRfc3BhdGlhbF9tb2RlbCkKc3RkX21udGRfYW5hbHlzaXNfcHJlZF9wbG90CmBgYAoKCiMjIEdhcGUgd2lkdGggLSBGRGl2CmBgYHtyfQpzdGRfYmVha193aWR0aF9mZGl2X3NwYXRpYWxfcGxvdCA9IGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKQpzdGRfYmVha193aWR0aF9mZGl2X3NwYXRpYWxfcGxvdCA9IHdpdGhfcmVhbG1zKHN0ZF9iZWFrX3dpZHRoX2ZkaXZfc3BhdGlhbF9wbG90KQpzdGRfYmVha193aWR0aF9mZGl2X3NwYXRpYWxfcGxvdApgYGAKCmBgYHtyfQpzdGRfYmVha193aWR0aF9mZGl2X2FuYWx5c2lzX2dlb19wbG90ID0gZ2VvbV9tYXAoZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gYmVha193aWR0aF9mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdCZWFrIFdpZHRoIEZEaXYnKQpzdGRfYmVha193aWR0aF9mZGl2X2FuYWx5c2lzX2dlb19wbG90CmBgYAoKYGBge3J9CmJlYWtfd2lkdGhfZm9ybXVsYSA9IGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKQpmaW5kX2FpY3NfZm9yX2NvcnJlbGF0aW9ucyhiZWFrX3dpZHRoX2Zvcm11bGEpCmBgYAoKYGBge3J9CmJlYWtfd2lkdGhfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwoYmVha193aWR0aF9mb3JtdWxhLCBjb3JMaW4oZm9ybSA9IGNvcnJlbGF0aW9uX2Zvcm11bGEpKQpgYGAKCmBgYHtyfQpzdGRfYmVha193aWR0aF9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfcmVzdWx0KGJlYWtfd2lkdGhfc3BhdGlhbF9tb2RlbCkKc3RkX2JlYWtfd2lkdGhfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QKYGBgCgoKIyMgSFdJIC0gRkRpdgpgYGB7cn0Kc3RkX2h3aV9mZGl2X3NwYXRpYWxfcGxvdCA9IGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IGh3aV9mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIlN0YW5kYXJkaXNlZCByZXNwb25zZSIpCnN0ZF9od2lfZmRpdl9zcGF0aWFsX3Bsb3QgPSB3aXRoX3JlYWxtcyhzdGRfaHdpX2ZkaXZfc3BhdGlhbF9wbG90KQpzdGRfaHdpX2ZkaXZfc3BhdGlhbF9wbG90CmBgYAoKYGBge3J9CnN0ZF9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwKGdlb21fc2YoZGF0YSA9IGFuYWx5c2lzX2RhdGEsIGFlcyhjb2xvciA9IGh3aV9mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdIV0kgRkRpdicpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdApgYGAKCmBgYHtyfQpod2lfZm9ybXVsYSA9IGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpCmZpbmRfYWljc19mb3JfY29ycmVsYXRpb25zKGh3aV9mb3JtdWxhKQpgYGAKCmBgYHtyfQpod2lfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwoaHdpX2Zvcm11bGEsIGNvckxpbihmb3JtID0gY29ycmVsYXRpb25fZm9ybXVsYSkpCmBgYAoKYGBge3J9CnN0ZF9od2lfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgPSBwbG90X3Jlc3VsdChod2lfc3BhdGlhbF9tb2RlbCkKc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdApgYGAKCgojIyBNYXNzIC0gRkRpdgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9zcGF0aWFsX3Bsb3QgPSBnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvdXIgPSBod2lfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKQpzdGRfbWFzc19mZGl2X3NwYXRpYWxfcGxvdCA9IHdpdGhfcmVhbG1zKHN0ZF9tYXNzX2ZkaXZfc3BhdGlhbF9wbG90KQpzdGRfbWFzc19mZGl2X3NwYXRpYWxfcGxvdApgYGAKCmBgYHtyfQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2dlb19wbG90ID0gZ2VvbV9tYXAoZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gbWFzc19mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdNYXNzIEZEaXYnKQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2dlb19wbG90CmBgYAoKYGBge3J9Cm1hc3NfZm9ybXVsYSA9IGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKQpmaW5kX2FpY3NfZm9yX2NvcnJlbGF0aW9ucyhtYXNzX2Zvcm11bGEpCmBgYAoKYGBge3J9Cm1hc3Nfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwobWFzc19mb3JtdWxhLCBjb3JHYXVzKGZvcm0gPSBjb3JyZWxhdGlvbl9mb3JtdWxhKSkKYGBgCgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgPSBwbG90X3Jlc3VsdChtYXNzX3NwYXRpYWxfbW9kZWwpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90CmBgYAoKCgojIENyZWF0ZSBwbG90IG9mIGRpZmZlcmVuY2VzIGluIHByb2Nlc3MgcmVzcG9uc2UKYGBge3J9CnByZWRfbGVnZW5kIDwtIGdncHVicjo6Z2V0X2xlZ2VuZCgKICAjIGNyZWF0ZSBzb21lIHNwYWNlIHRvIHRoZSBsZWZ0IG9mIHRoZSBsZWdlbmQKICBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWUobGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCkpICsgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKyBsYWJzKGNvbG9yID0gIlByZWRpY3RvciB0eXBlIikKKQpnZW9fbGVnZW5kIDwtIGdncHVicjo6Z2V0X2xlZ2VuZCgKICAjIGNyZWF0ZSBzb21lIHNwYWNlIHRvIHRoZSBsZWZ0IG9mIHRoZSBsZWdlbmQKICBzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2dlb19wbG90ICsgdGhlbWUobGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oLTgwLCAwLCAwLCAxMiksIGxlZ2VuZC50aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgxMCwgJ21tJykpICsgbGFicyhjb2xvciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKQopCgpsZWdlbmQgPSBwbG90X2dyaWQoCiAgZ2VvX2xlZ2VuZCwKICBwcmVkX2xlZ2VuZCwgCiAgbnJvdyA9IDEKKQpsZWdlbmQKYGBgCgpgYGB7cn0KcGxvdF9ncmlkKAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9tbnRkX2FuYWx5c2lzX2dlb19wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksIAogICAgc3RkX21udGRfYW5hbHlzaXNfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksIAogICAgc3RkX21udGRfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0zLCAzKSkgKyB5bGFiKCcnKSwgCiAgICBucm93ID0gMQogICkgKyBkcmF3X2xhYmVsKCJNTlREIiwgc2l6ZSA9IDE2LCBhbmdsZSA9IDkwLCB4ID0gMC4wMSwgeSA9IDAuNSksCiAgcGxvdF9ncmlkKAogICAgc3RkX2JlYWtfd2lkdGhfZmRpdl9hbmFseXNpc19nZW9fcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLCAKICAgIHN0ZF9iZWFrX3dpZHRoX2ZkaXZfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksIAogICAgc3RkX2JlYWtfd2lkdGhfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIG5yb3cgPSAxCiAgKSArIGRyYXdfbGFiZWwoIkJlYWsgV2lkdGgiLCBzaXplID0gMTYsIGFuZ2xlID0gOTAsIHggPSAwLjAxLCB5ID0gMC41KSwKICBwbG90X2dyaWQoCiAgICBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwgCiAgICBzdGRfaHdpX2ZkaXZfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksIAogICAgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiSFdJIiwgc2l6ZSA9IDE2LCBhbmdsZSA9IDkwLCB4ID0gMC4wMSwgeSA9IDAuNSksCiAgcGxvdF9ncmlkKAogICAgc3RkX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLCAKICAgIHN0ZF9tYXNzX2ZkaXZfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksIAogICAgc3RkX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIG5yb3cgPSAxCiAgKSArIGRyYXdfbGFiZWwoIk1hc3MiLCBzaXplID0gMTYsIGFuZ2xlID0gOTAsIHggPSAwLjAxLCB5ID0gMC41KSwgCiAgbGVnZW5kLAogIG5yb3cgPSA1CikKZ2dzYXZlKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ3Byb2Nlc3NfcmVzcG9uc2UuanBnJyksIHdpZHRoID0gNDIwMCwgaGVpZ2h0ID0gMzIwMCwgdW5pdHMgPSAncHgnKQpgYGAKCgojIENvbXBhcmUgbWV0cmljcyBhZ2FpbnN0IGVhY2ggb3RoZXIKYGBge3J9CmdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgeSA9IG1udGRfc3RhbmRhcmQsIGNvbG91ciA9IGNvcmVfcmVhbG0pKSArIAogIGdlb21fcG9pbnQoKSArCiAgeWxhYigiTU5URCIpICsgCiAgeGxhYigiQmVhayBXaWR0aCBGRGl2IikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKGNvbG9yID0gIlJlYWxtIikKYGBgCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gaHdpX2ZkaXZfc3RhbmRhcmQsIHkgPSBtbnRkX3N0YW5kYXJkLCBjb2xvdXIgPSBjb3JlX3JlYWxtKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHlsYWIoIk1OVEQiKSArIAogIHhsYWIoIkhXSSBGRGl2IikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKGNvbG9yID0gIlJlYWxtIikKYGBgCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gaHdpX2ZkaXZfc3RhbmRhcmQsIHkgPSBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIGNvbG91ciA9IGNvcmVfcmVhbG0pKSArIAogIGdlb21fcG9pbnQoKSArCiAgeWxhYigiQmVhayBXaWR0aCBGRGl2IikgKyAKICB4bGFiKCJIV0kgRkRpdiIpICsKICB0aGVtZV9idygpICsgbGFicyhjb2xvciA9ICJSZWFsbSIpCmBgYAoKYGBge3J9Cm1udGRfZmRpdl9hbmFseXNpcyA9IGFuYWx5c2lzX2RhdGEgJT4lIAogIGRwbHlyOjpzZWxlY3QoY2l0eV9pZCwgIG1udGRfc3RhbmRhcmQsIGh3aV9mZGl2X3N0YW5kYXJkLCBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIG1hc3NfZmRpdl9zdGFuZGFyZCkgJT4lCiAgbGVmdF9qb2luKGNvbW11bml0eV9zdW1tYXJ5KSAlPiUKICBtdXRhdGUodXJiYW5fcG9vbF9wZXJjID0gdXJiYW5fcG9vbF9zaXplICogMTAwIC8gcmVnaW9uYWxfcG9vbF9zaXplKQptbnRkX2ZkaXZfYW5hbHlzaXMKYGBgCgpgYGB7cn0KZ2dwYWlycyhtbnRkX2ZkaXZfYW5hbHlzaXMgJT4lIGRwbHlyOjpzZWxlY3QobW50ZF9zdGFuZGFyZCwgaHdpX2ZkaXZfc3RhbmRhcmQsIGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbWFzc19mZGl2X3N0YW5kYXJkLCByZWdpb25hbF9wb29sX3NpemUsIHVyYmFuX3Bvb2xfc2l6ZSwgdXJiYW5fcG9vbF9wZXJjKSwgY29sdW1uTGFiZWxzID0gYygnTU5URCcsICdIV0kgRkQnLCAnQmsgRkQnLCAnTXNzIEZEJywgJ1JlZ2lvbiBSaWNoLicsICdVcmJhbiBSaWNoLicsICclIFVyYmFuJykpCmdnc2F2ZShmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdhcHBlbmRpeF9zdGFuZGFyaXNlZF9jb3JyZWxhdGlvbi5qcGcnKSkKYGBgCgoK